package com.yyfax.pay.order.data.service.impl;

import java.util.Date;
import java.util.HashMap;

import javax.annotation.Resource;

import com.yyfax.pay.third.facade.fuiou.ProtocolAdaptService;
import com.yyfax.pay.third.facade.fuiou.ProtocolService;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;

import com.fuiou.mpay.encrypt.DESCoderFUIOU;
import com.fuiou.util.MD5;
import com.google.common.collect.Maps;
import com.yyfax.commons.http.HttpUtil;
import com.yyfax.commons.json.FastJsonUtil;
import com.yyfax.commons.lang.DateUtil;
import com.yyfax.framework.common.Log;
import com.yyfax.framework.common.OpResponse;
import com.yyfax.framework.yyfax.enums.YesNoEnum;
import com.yyfax.pay.common.constants.FuiouProtocolPayConstant;
import com.yyfax.pay.common.constants.PayConstants;
import com.yyfax.pay.common.enums.*;
import com.yyfax.pay.common.exception.PayExceptionCode;
import com.yyfax.pay.common.model.to.YYPayApiReq2;
import com.yyfax.pay.common.util.FuiouIdUtils;
import com.yyfax.pay.order.data.dao.protocol.ProtocolOrderDao;
import com.yyfax.pay.order.data.service.ProtocolPayService;
import com.yyfax.pay.order.data.service.UserPaymentService;
import com.yyfax.pay.order.model.domain.ProtocolOrderDO;
import com.yyfax.pay.order.model.po.NotifyTaskPO;
import com.yyfax.pay.order.model.po.UserPaymentPO;
import com.yyfax.pay.order.model.vo.ProtocolPayQueryVO;
import com.yyfax.pay.order.model.vo.ProtocolPayVO;
import com.yyfax.pay.order.service.PayCommonService;
import com.yyfax.pay.quartz.service.PayTaskService;
import com.yyfax.pay.third.facade.fuiou.FuiouProtocolService;
import com.yyfax.pay.third.model.to.fuiou.*;
import com.yyfax.pay.vo.order.*;

@Service
public class ProtocolPayServiceImpl implements ProtocolPayService {

	@Resource
	private ProtocolOrderDao protocolOrderDao;

	@Resource
	private FuiouIdUtils fuiouIdUtils;

	@Resource
	private UserPaymentService userPaymentService;

	@Resource
	private FuiouProtocolPayConstant fuiouProtocolPayConstant;

	@Resource
	private ProtocolAdaptService protocolAdaptService;

	protected Logger logger = LoggerFactory.getLogger(getClass());

	/**
	 * 协议支付
	 * 
	 * @author wangjwy
	 * @date 2018/6/7 16:01
	 * @since 2.2.0
	 * @param params, merchantCode
	 * @return void
	 */
	@Override
	public OpResponse pay(ProtocolPayReq params, String merchantCode) {
		final String OP = "ProtocolPayServiceImpl.pay";
		// 保存订单
		UserPaymentPO query = userPaymentService.query(merchantCode, params.getUserId(), params.getPaymentCode());
		if (null == query || StringUtils.isBlank(query.getProtocolCode()) || StringUtils.equals(query.getState(), YesNoEnum.NO.getValue())) {
			logger.error(Log.op(OP).msg("未开通对应支付通道,或者协议号缺失").kv("params", params).toString());
			throw PayExceptionCode.ERROR_PAYMENT_OR_PROTOCOLCODE_NOT_EXIST.exp();
		}

		ProtocolOrderDO po = buildProtocolOrder(params, merchantCode, query);
		OpResponse response = new OpResponse();
		try {
			// 持久化订单
			protocolOrderDao.insert(po);
		} catch (DuplicateKeyException e) {
			ProtocolOrderDO existPO = protocolOrderDao.selectByMerchantCodeAndOrderId(po.getMerchantCode(), po.getOrderId());
			if (null == existPO) {
				logger.error(Log.op(OP).msg("保存协议支付订单主键冲突").kv("ProtocolOrderDO", po).toString());
				throw PayExceptionCode.SYS_ERROR.exp();
			}
			logger.info(Log.op(OP).msg("保存协议支付订单幂等成功").kv("existPO", existPO).toString());

			response.setCode(existPO.getRespCode() == null ? PayExceptionCode.DEALING.getCode() : existPO.getRespCode());
			response.setDisplay(existPO.getRespDesc());
			return response;
		}

		// 更新订单状态
		int mCount = protocolOrderDao.modifyState(po.getBusiId(), Integer.valueOf(OrderStateEnum.DEALING.getValue()),
				Integer.valueOf(OrderStateEnum.INI.getValue()), null, null, null);
		if (1 != mCount) {
			logger.error(Log.op(OP).msg("协议支付更改订单状态未命中").kv("po", po).toString());
			throw PayExceptionCode.ERROR_ORDER_STATE_NOT_INIT.exp();
		}

		ProtocolService adaptiveTradeService = protocolAdaptService.getAdaptiveTradeService(ProtocolEnum.form(po.getActPaymentCode()));
		return adaptiveTradeService.Pay(po, query);

	}

	/**
	 * 处理异步通知
	 * 
	 * @author wangjwy
	 * @date 2018/6/8 14:09
	 * @since 2.2.0
	 * @param req
	 * @return void
	 * @throws Exception
	 */
	@Override
	public void handNotifyResult(ProtocolPayNotifyReq req) {
		final String OP = "ProtocolPayServiceImpl.handNotifyResult";
		logger.info(Log.op(OP).msg("收到协议支付异步通知").kv("req", req).toString());
		if (null == req || StringUtils.isBlank(req.getMchntorderid())) {
			logger.warn(Log.op(OP).msg("协议支付返回参数异常,不做处理").kv("req", req).toString());
			return;
		}

		ProtocolOrderDO po = protocolOrderDao.selectByPrimaryKey(req.getMchntorderid());
		if (null == po) {
			logger.error(Log.op(OP).msg("协议支付异步返回订单号,查询订单不存在").kv("busiId", req.getMchntorderid()).kv("thirdOrderid", req.getOrderid()).toString());
			// throw PayExceptionCode.WARN_UNKNOWN.exp();
			return;
		}

		// 获取用户支付凭证信息
		UserPaymentPO userPaymentPO = userPaymentService.query(po.getMerchantCode(), po.getUserId(), po.getPaymentCode());
		if (null == userPaymentPO || StringUtils.equals(userPaymentPO.getState(), YesNoEnum.NO.getValue())) {
			logger.error(Log.op(OP).msg("接收异步通知查询到用户支付凭证信息不存在").kv("req", req).toString());
			// throw PayExceptionCode.ERROR_USER_PAYMENT_NOT_EXISTS.exp();
		}

		ProtocolService adaptiveTradeService = protocolAdaptService.getAdaptiveTradeService(ProtocolEnum.form(po.getActPaymentCode()));
		adaptiveTradeService.handNotify(req, po, userPaymentPO);
	}

	/**
	 * 查询协议支付结果
	 * 
	 * @author wangjwy
	 * @date 2018/6/8 20:00
	 * @since 2.2.0
	 * @param params
	 * @return com.yyfax.pay.vo.order.ProtocolPayQueryRsp
	 * @throws Exception
	 */
	@Override
	public OpResponse checkResult(ProtocolPayQueryReq params, String merchantCode) {
		final String OP = "ProtocolPayServiceImpl.checkResult";
		ProtocolOrderDO po = protocolOrderDao.selectByMerchantCodeAndOrderId(merchantCode, params.getOrderId());
		OpResponse response = OpResponse.success();
		if (null == po) {
			logger.info(Log.op(OP).msg("查询订单为空,直接返回").kv("orderId", params.getOrderId()).toString());
			throw PayExceptionCode.ORDER_NOT_FOUND.exp();
		}
		if (OrderStateEnum.isResult(po.getState().toString())) {
			logger.info(Log.op(OP).msg("查询到订单已经出结果,直接返回结果").kv("ProtocolOrderDO", po).toString());
			response.setCode(PayExceptionCode.SUCCESS.getCode());
			response.setContent(buildProtocolQueryRsp(po));
			return response;
		}
		ProtocolService adaptiveTradeService = protocolAdaptService.getAdaptiveTradeService(ProtocolEnum.form(po.getActPaymentCode()));
		return adaptiveTradeService.checkResult(po);
	}

	/**
	 * 构建响应
	 * 
	 * @author wangjwy
	 * @date 2018/6/19 10:55
	 * @since 2.2.0
	 * @param po
	 * @return com.yyfax.pay.vo.order.ProtocolPayQueryRsp
	 * @throws Exception
	 */
	private ProtocolPayQueryRsp buildProtocolQueryRsp(ProtocolOrderDO po) {
		ProtocolPayQueryRsp rsp = new ProtocolPayQueryRsp();
		rsp.setAmount(po.getAmount());
		rsp.setBusiId(po.getBusiId());
		rsp.setMerchantCode(po.getMerchantCode());
		rsp.setOrderId(po.getOrderId());
		rsp.setBusiId(po.getBusiId());
		rsp.setOrderTime(po.getOrderTime());
		rsp.setProtocolCode(po.getProtocolCode());
		rsp.setRespCode(po.getRespCode());
		rsp.setRespDesc(po.getRespDesc());
		rsp.setState(po.getState());
		rsp.setUserId(po.getUserId());
		return rsp;
	}

	/**
	 * 构建订单实体
	 * 
	 * @author wangjwy
	 * @date 2018/6/7 16:11
	 * @since 2.2.0
	 * @param params, merchantCode
	 * @return com.yyfax.pay.order.model.domain.ProtocolOrderDO
	 * @throws Exception
	 */
	private ProtocolOrderDO buildProtocolOrder(ProtocolPayReq params, String merchantCode, UserPaymentPO query) {
		final String OP = "ProtocolPayServiceImpl.buildProtocolOrder";
		ProtocolOrderDO po = new ProtocolOrderDO();
		po.setActPaymentCode(params.getPaymentCode());
		po.setAmount(params.getAmount());
		po.setBusiId(fuiouIdUtils.generateBusiId(FuiouBusiTypeEnum.PROTOCOL_PAY, merchantCode));
		po.setUserIp(params.getUserIp());
		po.setCurrency(params.getCurrency().toString());
		po.setDataSrc(params.getDataSrc());
		po.setIsNotify(YesNoEnum.NO.getValue());
		po.setMerchantCode(merchantCode);
		po.setNotifyUrl(fuiouProtocolPayConstant.getProtocolPayNotifUrl());
		po.setOrderId(params.getOrderId());
		po.setOrderTime(params.getOrderTime());
		po.setPaymentCode(params.getPaymentCode());
		po.setPayNotifyUrl(params.getPayNotifyUrl());
		po.setProtocolCode(query.getProtocolCode());
		po.setState(Integer.valueOf(OrderStateEnum.INI.getValue()));
		po.setTerminal(params.getTerminal());
		po.setUserId(params.getUserId());
		po.setUserAuthId(query.getUserAuthId());
		return po;
	}

	/**
	 * 查询
	 * 
	 * @author wangjwy
	 * @date 2018/6/19 9:53
	 * @since 2.2.0
	 * @param busiId
	 * @return boolean
	 * @throws Exception
	 */
	@Override
	public boolean queryResult(String busiId) {
		final String OP = "ProtocolPayServiceImpl.queryResult";
		ProtocolOrderDO protocolOrderDO = protocolOrderDao.selectByPrimaryKey(busiId);
		if (null == protocolOrderDO) {
			logger.warn(Log.op(OP).msg("查询到订单不存在").kv("busiId", busiId).toString());
			return true;
		}
		ProtocolPayQueryReq req = new ProtocolPayQueryReq();
		req.setPaymentCode(PaymentEnum.FUIOU_PROTOCOL_PAY.getValue());
		req.setOrderId(protocolOrderDO.getOrderId());
		try {
			OpResponse response = checkResult(req, protocolOrderDO.getMerchantCode());
			ProtocolPayQueryRsp content = (ProtocolPayQueryRsp) response.getContent();
			if (null == content) {
				return false;
			}
			if (OrderStateEnum.DEALING.getValue().equals(content.getState().toString())) {
				// 为处理中
				return false;
			}
		} catch (Exception e) {
			logger.warn(Log.op(OP).msg("查询订单结果异常").kv("busiId", protocolOrderDO.getBusiId()).toString(), e);
			return false;
		}
		return true;
	}

	/**
	 * 通知业务方
	 * 
	 * @author wangjwy
	 * @date 2018/6/19 9:53
	 * @since 2.2.0
	 * @param task
	 * @return java.lang.String
	 */
	@Override
	public String notifyProtocolOrder(NotifyTaskPO task) {
		final String OP = "ProtocolPayServiceImpl.notifyProtocolOrder";
		logger.info(Log.op(OP).msg("异步通知业务方").kv("param", task.getNotifyUrl()).toString());
		String rspJson = HttpUtil.post(task.getNotifyUrl(), task.getReqContent());
		if (StringUtils.isBlank(rspJson)) {
			logger.info("回调失败:{}", task.getOrderId());
		}
		return rspJson;
	}
}
